﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.IO;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using System.Diagnostics;


namespace Algoritmo_di_Dijkstra
{
    public partial class FormMain : Form
    {
        //Via Ronchi(23262563)
        //Via Tolmezzo(24387213)
        //Piazza Udine(23563428)
        //Via Palmanova(4861389)
        //Via Crescenzago(23566357)
        // <bounds minlat="45.4903000" minlon="9.2353000" maxlat="45.5164000" maxlon="9.2656000"/>
        // <bounds minlat="45.4809000" minlon="9.2116000" maxlat="45.5245000" maxlon="9.2902000"/>

        //Pens and graphics
        Pen pen_Route = new Pen(Color.FromArgb(211, 42, 226), 3);
        Brush brushRoutePoints = new SolidBrush(Color.FromArgb(211, 42, 226));

        ArrayList searchWayResults = new ArrayList();
        public static System.Globalization.NumberFormatInfo numberFormat_EnUS = new System.Globalization.CultureInfo("en-US", false).NumberFormat;

        Map map = new Map();

        public double bounds_world_min_lat = 45.4809000d;
        public double bounds_world_min_lon = 9.2116000d;
        public double bounds_world_max_lat = 45.5245000d;
        public double bounds_world_max_lon = 9.2902000d;
        public double diff_lat;
        public double diff_lon;
        public double world_Width;
        public double world_Height;

        public FormMain()
        {
            InitializeComponent();
        }
        private void FormMain_Load(object sender, EventArgs e)
        {
            XmlReader reader = XmlReader.Create("UdineRoute.osm");

            DateTime loadingStartTime = DateTime.Now;

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "bounds")
                    {
                        bounds_world_min_lat = double.Parse(reader.GetAttribute("minlat"), numberFormat_EnUS);
                        bounds_world_min_lon = double.Parse(reader.GetAttribute("minlon"), numberFormat_EnUS);
                        bounds_world_max_lat = double.Parse(reader.GetAttribute("maxlat"), numberFormat_EnUS);
                        bounds_world_max_lon = double.Parse(reader.GetAttribute("maxlon"), numberFormat_EnUS);
                    }

                    if (reader.Name == "node")
                    {
                        MapNode nuovoNodo = new MapNode();
                        nuovoNodo.id = long.Parse(reader.GetAttribute("id"));
                        nuovoNodo.x = double.Parse(reader.GetAttribute("lon"), numberFormat_EnUS);
                        nuovoNodo.y = double.Parse(reader.GetAttribute("lat"), numberFormat_EnUS);
                        map.addNode(nuovoNodo);
                    }

                    if (reader.Name == "way")
                    {
                        long idWay = long.Parse(reader.GetAttribute("id"));
                        int connectionCounter = 1;
                        long idNodeTMP = -1;
                        bool isBuilding = false;

                        MapWay newWay = new MapWay();
                        newWay.id = idWay;

                        MapBuilding newBuilding = new MapBuilding();
                        newBuilding.id = idWay;

                        XmlReader readerChilds = reader.ReadSubtree();

                        while (readerChilds.Read())
                        {
                            if (readerChilds.Name == "nd")
                            {
                                long idNode = long.Parse(readerChilds.GetAttribute("ref")); ;

                                if (idNodeTMP != -1)
                                {
                                    MapConnection newConnection = new MapConnection();
                                    newConnection.idWay = idWay;
                                    newConnection.idConnection = connectionCounter;

                                    MapNode aNode = new MapNode();
                                    aNode.id = idNodeTMP;
                                    newConnection.a = aNode;

                                    MapNode bNode = new MapNode();
                                    bNode.id = idNode;
                                    newConnection.b = bNode;

                                    map.addConnection(newConnection);
                                    connectionCounter++;
                                }

                                idNodeTMP = idNode;
                            }

                            if (readerChilds.Name == "tag")
                            {
                                string key = readerChilds.GetAttribute("k").Replace("\"", "").Replace("'", "''");
                                string value = readerChilds.GetAttribute("v").Replace("\"", "").Replace("'", "''");

                                if (key == "highway")
                                {
                                    newWay.type = value;
                                }

                                if (key == "oneway")
                                {
                                    if (value == "yes")
                                        newWay.oneWay = true;
                                    else
                                        newWay.oneWay = false;
                                }

                                if (key == "building")
                                {
                                    isBuilding = true;
                                }

                                if (key == "name")
                                {
                                    newWay.name = value;
                                    newBuilding.name = value;
                                }
                            }
                        }

                        if (isBuilding)
                            map.addBuilding(newBuilding);
                        else
                            if (newWay.name != "n/a")
                                map.addWay(newWay);
                    }
                }
            }

            foreach (MapConnection tmpMapConn in map.MapConnections.GetValueList())
            {
                PopolaDatiNodi(tmpMapConn);
            }

            TimeSpan loadTime = DateTime.Now - loadingStartTime;
            richTextBox1.Text += "Tempo caricamento: " + loadTime.ToString() + "\n";
            richTextBox1.Text += "Nodi caricati: " + map.MapNodes.Count + "\n";
            richTextBox1.Text += "Connection caricate: " + map.MapConnections.Count + "\n";
            richTextBox1.Text += "Ways caricate: " + map.MapWays.Count + "\n\n";


            //for (int c = 0; c < 10; c++)
            //{
            //    MapNode tmpNode = (MapNode)map.MapNodes.GetValueList()[c];
            //    richTextBox1.Text += tmpNode.id + "(" + tmpNode.x + ", " + tmpNode.y + ")\n";
            //}            

            //for (int c = 0; c < 100; c++)
            //{
            //    MapConnection tmpConnection = (MapConnection)map.MapConnections.GetValueList()[c];
            //    richTextBox1.Text += tmpConnection.idConnection + "\t" + tmpConnection.idWay + "\t" + tmpConnection.distance + "\n";            
            //}

            for (int c = 0; c < 100; c++)
            {
                MapWay tmpWay = (MapWay)map.MapWays.GetValueList()[c];
                richTextBox1.Text += tmpWay.id + "\t" + tmpWay.name + "\n";
            }
        }
        private void PopolaDatiNodi(MapConnection tmpMapConn)
        {
            tmpMapConn.a = (MapNode)map.MapNodes[tmpMapConn.a.id];
            tmpMapConn.b = (MapNode)map.MapNodes[tmpMapConn.b.id];

            tmpMapConn.distance = GetDistance(tmpMapConn.a.x, tmpMapConn.a.y, tmpMapConn.b.x, tmpMapConn.b.y);
        }
        private float GetDistance(PointF point1, PointF point2)
        {
            //pythagoras theorem c^2 = a^2 + b^2
            //thus c = square root(a^2 + b^2)
            double a = (double)(point2.X - point1.X);
            double b = (double)(point2.Y - point1.Y);

            return (float)Math.Sqrt(a * a + b * b);
        }
        private double GetDistance(double x1, double y1, double x2, double y2)
        {
            //pythagoras theorem c^2 = a^2 + b^2
            //thus c = square root(a^2 + b^2)
            double a = (double)(x2 - x1);
            double b = (double)(y2 - y1);

            return Math.Sqrt(a * a + b * b);
        }
        private void PaintViewportDijkstra(Panel panelToRender)
        {
            DateTime RenderingStartTime = DateTime.Now;

            //Creating layer images to draw
            //Bitmap rotatingImage = new Bitmap(panelToRender.Width, panelToRender.Height);
            //Graphics graphicsOfRotatingImage = Graphics.FromImage(rotatingImage);
            Graphics g = panelToRender.CreateGraphics();

            diff_lat = bounds_world_max_lat - bounds_world_min_lat;
            diff_lon = bounds_world_max_lon - bounds_world_min_lon;
            world_Width = bounds_world_max_lon - bounds_world_min_lon;
            world_Height = bounds_world_max_lat - bounds_world_min_lat;

            //Nodes...
            foreach (MapNode tmpNode in map.MapNodes.GetValueList())
            {
                double point_world_X = bounds_world_max_lon - tmpNode.x;
                double point_world_Y = bounds_world_max_lat - tmpNode.y;
                int point_screen_X = 0;
                int point_screen_Y = 0;

                // world_Width : screen_Width = point_world_X : point_screen_X
                point_screen_X = panelToRender.Width - (int)(panelToRender.Width * point_world_X / world_Width);
                // world_Height : screen_Height = point_world_Y : point_screen_Y
                point_screen_Y = (int)(panelToRender.Height * point_world_Y / world_Height);

                //if ((point_screen_X <= panelToRender.Width) && (point_screen_X >= 0))
                //    if ((point_screen_Y <= panelToRender.Height) && (point_screen_Y >= 0))
                //        MessageBox.Show("sdfgsfd");

                //g.FillEllipse(Brushes.Black, point_screen_X, point_screen_Y, 10, 10);
                //g.DrawEllipse(Pens.Yellow, point_screen_X, point_screen_Y, 5, 5);
                //g.DrawString(tmpNode.id.ToString(), new System.Drawing.Font("Arial", 8), Brushes.Black, new Point(point_screen_X + 5, point_screen_Y - 5));

            }

            //Ways...
            foreach (MapConnection tmpConnection in map.MapConnections.GetValueList())
            {
                double point_world_X_a = bounds_world_max_lon - tmpConnection.a.x;
                double point_world_Y_a = bounds_world_max_lat - tmpConnection.a.y;
                int point_screen_X_a = 0;
                int point_screen_Y_a = 0;
                point_screen_X_a = panelToRender.Width - (int)(panelToRender.Width * point_world_X_a / world_Width);
                point_screen_Y_a = (int)(panelToRender.Height * point_world_Y_a / world_Height);

                double point_world_X_b = bounds_world_max_lon - tmpConnection.b.x;
                double point_world_Y_b = bounds_world_max_lat - tmpConnection.b.y;
                int point_screen_X_b = 0;
                int point_screen_Y_b = 0;
                point_screen_X_b = panelToRender.Width - (int)(panelToRender.Width * point_world_X_b / world_Width);
                point_screen_Y_b = (int)(panelToRender.Height * point_world_Y_b / world_Height);

                g.DrawLine(Pens.Green, point_screen_X_a, point_screen_Y_a, point_screen_X_b, point_screen_Y_b);
            }

            //Route...
            foreach (MapConnection tmpConnection in map.route.steps)
            {
                double point_world_X_a = bounds_world_max_lon - tmpConnection.a.x;
                double point_world_Y_a = bounds_world_max_lat - tmpConnection.a.y;
                int point_screen_X_a = 0;
                int point_screen_Y_a = 0;
                point_screen_X_a = panelToRender.Width - (int)(panelToRender.Width * point_world_X_a / world_Width);
                point_screen_Y_a = (int)(panelToRender.Height * point_world_Y_a / world_Height);

                double point_world_X_b = bounds_world_max_lon - tmpConnection.b.x;
                double point_world_Y_b = bounds_world_max_lat - tmpConnection.b.y;
                int point_screen_X_b = 0;
                int point_screen_Y_b = 0;
                point_screen_X_b = panelToRender.Width - (int)(panelToRender.Width * point_world_X_b / world_Width);
                point_screen_Y_b = (int)(panelToRender.Height * point_world_Y_b / world_Height);


                g.FillEllipse(brushRoutePoints, point_screen_X_a, point_screen_Y_a, 5, 5);
                g.FillEllipse(brushRoutePoints, point_screen_X_b, point_screen_Y_b, 5, 5);
                g.DrawLine(pen_Route, point_screen_X_a, point_screen_Y_a, point_screen_X_b, point_screen_Y_b);

            }


        }
        private void button1_Click(object sender, EventArgs e)
        {
            PaintViewportDijkstra(panel1);
        }
        private void textBox_SearchRoad_TextChanged(object sender, EventArgs e)
        {
            searchWayResults = map.searchRoad(textBox_SearchRoad.Text);

            listBox_Search.Items.Clear();

            foreach (MapWaySearchResult tmpRes in searchWayResults)
            {
                listBox_Search.Items.Add(tmpRes.name + "(" + tmpRes.id + ")");
            }
        }
        private void listBox_Search_DoubleClick(object sender, EventArgs e)
        {

        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            PaintViewportDijkstra(panel1);
        }

        private void button_AddPartenza_Click(object sender, EventArgs e)
        {
            if (listBox_Search.SelectedIndex >= 0)
            {
                MapWaySearchResult selectedWay = (MapWaySearchResult)searchWayResults[listBox_Search.SelectedIndex];
                map.routeStart = selectedWay.originalMapWay;
                textBox_Partenza.Text = map.routeStart.ToString();
                //richTextBox2.Text += selectedWay.name + "(" + selectedWay.id + ")";
            }
        }

        private void button_AddArrivo_Click(object sender, EventArgs e)
        {
            if (listBox_Search.SelectedIndex >= 0)
            {
                MapWaySearchResult selectedWay = (MapWaySearchResult)searchWayResults[listBox_Search.SelectedIndex];
                map.routeArrival = selectedWay.originalMapWay;
                textBox_Arrivo.Text = map.routeArrival.ToString();
                //richTextBox2.Text += selectedWay.name + "(" + selectedWay.id + ")";
            }
        }

        private void button_CalcolaRoute_Click(object sender, EventArgs e)
        {
            button_CalcolaRoute.Enabled = false;

            map.CalcolaRoute();
            PaintViewportDijkstra(panel1);

            button_CalcolaRoute.Enabled = true;
        }

        //public void PaintViewport_new(Panel panelViewport)
        //{
        //    DateTime RenderingStartTime = DateTime.Now;

        //    //Creating layer images to draw
        //    Bitmap rotatingImage = new Bitmap(panelViewport.Width, panelViewport.Height);
        //    Graphics graphicsOfRotatingImage = Graphics.FromImage(rotatingImage);
        //    Bitmap fixedImage = new Bitmap(panelViewport.Width, panelViewport.Height);
        //    Graphics graphicsOfFixedImage = Graphics.FromImage(fixedImage);

        //    //Coordinates window      
        //    float diff_lat = osmMap.bounds_world_max_lat - osmMap.bounds_world_min_lat;
        //    float diff_lon = osmMap.bounds_world_max_lon - osmMap.bounds_world_min_lon;
        //    float world_Width = osmMap.bounds_world_max_lon - osmMap.bounds_world_min_lon;
        //    float world_Height = osmMap.bounds_world_max_lat - osmMap.bounds_world_min_lat;
        //    int screen_Height = panelViewport.Height;
        //    int screen_Width = panelViewport.Width;

        //    //CAR coordinates
        //    float CAR_point_world_X = osmMap.bounds_world_max_lon - osmMap.CAR_position.X;
        //    float CAR_point_world_Y = osmMap.bounds_world_max_lat - osmMap.CAR_position.Y;
        //    int CAR_point_screen_X = screen_Width - (int)(screen_Width * CAR_point_world_X / world_Width);
        //    int CAR_point_screen_Y = (int)(screen_Height * CAR_point_world_Y / world_Height);

        //    graphicsOfRotatingImage.SmoothingMode = SmoothingMode.AntiAlias;
        //    graphicsOfRotatingImage.PixelOffsetMode = PixelOffsetMode.HighQuality;

        //    float direction = (float)osmMap.CAR_course;
        //    graphicsOfRotatingImage.TranslateTransform((float)CAR_point_screen_X, (float)CAR_point_screen_Y);
        //    graphicsOfRotatingImage.RotateTransform(-direction);
        //    graphicsOfRotatingImage.TranslateTransform(-(float)CAR_point_screen_X, -(float)CAR_point_screen_Y);

        //    //Background
        //    graphicsOfRotatingImage.FillRectangle(Brush_Map_Background, 0, 0, panelViewport.Width, panelViewport.Height);

        //    //X_db : X_panel = maxX_real : Width
        //    //X_panel = X_db * Width / maxX_real

        //    foreach (OSM_Way tmpWay in osmMap.GetWaysInView())
        //    {
        //        bool first = true;
        //        int previousNode_X = 0;
        //        int previousNode_Y = 0;

        //        Pen pen = Pen_Map_Roads;

        //        if (tmpWay.wayType == "motorway") pen = new Pen(Color.FromArgb(128, 155, 192), 8);
        //        if (tmpWay.wayType == "motorway_link") pen = new Pen(Color.FromArgb(128, 155, 192), 8);
        //        if (tmpWay.wayType == "trunk") pen = new Pen(Color.FromArgb(127, 201, 127), 7);
        //        if (tmpWay.wayType == "trunk_link") pen = new Pen(Color.FromArgb(127, 201, 127), 7);
        //        if (tmpWay.wayType == "primary") pen = new Pen(Color.FromArgb(228, 109, 113), 6);
        //        if (tmpWay.wayType == "primary_link") pen = new Pen(Color.FromArgb(228, 109, 113), 6);
        //        if (tmpWay.wayType == "secondary") pen = new Pen(Color.FromArgb(253, 191, 111), 5);
        //        if (tmpWay.wayType == "secondary_link") pen = new Pen(Color.FromArgb(253, 191, 111), 5);
        //        if (tmpWay.wayType == "tertiary") pen = new Pen(Color.FromArgb(252, 250, 116), 4);
        //        if (tmpWay.wayType == "residential") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
        //        if (tmpWay.wayType == "unclassified") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
        //        if (tmpWay.wayType == "road") pen = new Pen(Color.FromArgb(203, 203, 203), 3);
        //        if (tmpWay.wayType == "service") pen = new Pen(Color.FromArgb(253, 191, 111), 3);
        //        if (tmpWay.wayType == "footway") pen = new Pen(Color.FromArgb(234, 229, 157), 3);

        //        //if (tmpWay.wayType == "service") pen = new Pen(Color.Blue, 5);                    
        //        //if (tmpWay.wayType == "footway") pen = new Pen(Color.Green, 5);

        //        foreach (OSM_Node tmpNode in tmpWay.nodes)
        //        {
        //            float point_world_X = osmMap.bounds_world_max_lon - tmpNode.LON;
        //            float point_world_Y = osmMap.bounds_world_max_lat - tmpNode.LAT;

        //            //PointF pointTransformed = GlobalFunctions.rotatePoint(osmMap.CAR_position, new PointF(point_world_X, point_world_Y), (float)osmMap.CAR_course);

        //            //point_world_X = pointTransformed.X;
        //            //point_world_Y = pointTransformed.Y;

        //            int point_screen_X = 0;
        //            int point_screen_Y = 0;

        //            // world_Width : screen_Width = point_world_X : point_screen_X
        //            point_screen_X = screen_Width - (int)(screen_Width * point_world_X / world_Width);
        //            // world_Height : screen_Height = point_world_Y : point_screen_Y
        //            point_screen_Y = (int)(screen_Height * point_world_Y / world_Height);

        //            if ((point_screen_X >= 0) && (point_screen_Y >= 0))
        //                if (!first)
        //                {
        //                    graphicsOfRotatingImage.DrawLine(pen, previousNode_X, previousNode_Y, point_screen_X, point_screen_Y);
        //                    //graphicsOfImage.DrawBezier(pen, new Point(4, 7), new Point(44, 9), new Point(39, 72), new Point(13, 27));
        //                }

        //            first = false;

        //            previousNode_X = point_screen_X;
        //            previousNode_Y = point_screen_Y;
        //        }
        //    }

        //    if (osmMap.ViewBuildings)
        //        foreach (OSM_Building tmpBuilding in osmMap.GetBuildingsInView())
        //        {
        //            int previousNode_X = 0;
        //            int previousNode_Y = 0;

        //            Point[] pointsBuilding = new Point[tmpBuilding.nodes.Count];
        //            int nodesCounter = 0;
        //            GraphicsPath pathBuilding = new GraphicsPath();
        //            pathBuilding.StartFigure();

        //            foreach (OSM_Node tmpNode in tmpBuilding.nodes)
        //            {
        //                float point_world_X = osmMap.bounds_world_max_lon - tmpNode.LON;
        //                float point_world_Y = osmMap.bounds_world_max_lat - tmpNode.LAT;
        //                int point_screen_X = 0;
        //                int point_screen_Y = 0;

        //                // world_Width : screen_Width = point_world_X : point_screen_X
        //                point_screen_X = screen_Width - (int)(screen_Width * point_world_X / world_Width);
        //                // world_Height : screen_Height = point_world_Y : point_screen_Y
        //                point_screen_Y = (int)(screen_Height * point_world_Y / world_Height);

        //                if ((point_screen_X >= 0) && (point_screen_Y >= 0))
        //                    pointsBuilding[nodesCounter] = new Point(point_screen_X, point_screen_Y);

        //                nodesCounter++;

        //                previousNode_X = point_screen_X;
        //                previousNode_Y = point_screen_Y;
        //            }

        //            if (pointsBuilding.Length > 0)
        //            {
        //                Point firstPoint = pointsBuilding.First();
        //                Point LastPoint = pointsBuilding.Last();

        //                if ((firstPoint.X == LastPoint.X) && (firstPoint.Y == LastPoint.Y))
        //                    graphicsOfRotatingImage.FillPolygon(Brush_Map_Buildings, pointsBuilding);
        //            }
        //        }

        //    //CAR

        //    string pathCar2dImageFile = Settings.Skins_Path + "\\" + Settings.Skin_Name + "\\Gps\\car.png";

        //    if (File.Exists(pathCar2dImageFile))
        //    {
        //        Image carImage = Bitmap.FromFile(pathCar2dImageFile);

        //        int carImage_Position_X = CAR_point_screen_X - (carImage.Width / 2);
        //        int carImage_Position_Y = CAR_point_screen_Y - (carImage.Height / 2);

        //        graphicsOfRotatingImage.DrawImage(carImage, carImage_Position_X, carImage_Position_Y);
        //    }

        //    graphicsOfRotatingImage.DrawEllipse(Pen_Map_CAR, CAR_point_screen_X, CAR_point_screen_Y, 5, 5);

        //    graphicsOfRotatingImage.DrawString(osmMap.GetCarPositionString(), new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y - 14));
        //    graphicsOfRotatingImage.DrawString(osmMap.GetCarSpeedInKilometersPerHour() + " Km/h", new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y));
        //    graphicsOfRotatingImage.DrawString("Direction " + osmMap.CAR_course + "°", new System.Drawing.Font("Arial", 8), Brushes.Red, new Point(CAR_point_screen_X + 20, CAR_point_screen_Y + 14));


        //    //Satellites in view
        //    graphicsOfRotatingImage.DrawString("Satellites in view: " + visibleSatellites, new System.Drawing.Font("Arial", 8), Brushes.Gray, new PointF(50f, 24f));

        //    float shifter = 0f;

        //    foreach (GPGSV.Satellite tmp in satellites)
        //    {
        //        string txtSatellite = "(" + tmp.PRN + ") " + " \tElevation:" + tmp.Elevation + " \tPosition:" + tmp.Azimuth + " \tSignal:" + tmp.SNR;
        //        graphicsOfRotatingImage.DrawString(txtSatellite, new System.Drawing.Font("Arial", 8), Brushes.Gray, new PointF(180f, 24f + shifter));
        //        shifter += 14f;
        //    }


        //    //Tachimeter
        //    graphicsOfRotatingImage.DrawString(String.Format("{0:0.}", osmMap.GetCarSpeedInKilometersPerHour()) + " Km/h", new System.Drawing.Font("Arial", 24), Brushes.Blue, new Point(630, 350));

        //    //Where Am I
        //    if (null != osmMap.WhereAmI())
        //        graphicsOfRotatingImage.DrawString(osmMap.WhereAmI().name, new System.Drawing.Font("Arial", 12), Brushes.Blue, new Point(50, 360));




        //    //Image viewPortImageRotated = GlobalFunctions.RotateImage(viewportImage, (float)osmMap.CAR_course);




        //    //Render Image
        //    //graphics.DrawImage(viewportImage, panelGpsViewport.rectangle.Left, panelGpsViewport.rectangle.Top, panelGpsViewport.rectangle, GraphicsUnit.Pixel);

        //    LastRenderingTime = DateTime.Now - RenderingStartTime;

        //    ImageToPaint = rotatingImage;
        //}

    }
}
